#!/usr/bin/python3
# -*- coding: utf-8 -*-

from urllib import request
from urllib.error import URLError

import random, asyncio, re, os, sys, time, json, logging
import socket
import queue

import requests
import aiohttp
import socks
from Crypto.Cipher import AES

def getCols():
    import subprocess
    return int(subprocess.Popen(['tput','cols'], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0])
COLS = getCols()

def random_ip():
    a=random.randint(1,255)
    b=random.randint(1,255)
    c=random.randint(1,255)
    d=random.randint(1,255)
    return "%s.%s.%s.%s" % (str(a), str(b), str(c), str(d))

def random_Agent():
    agents = [
    "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36",
    "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:30.0) Gecko/20100101 Firefox/30.0",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/537.75.14",
    "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Win64; x64; Trident/6.0)",
    "Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11",
    "Opera/9.25 (Windows NT 5.1; U; en)",
    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
    "Mozilla/5.0 (compatible; Konqueror/3.5; Linux) KHTML/3.5.5 (like Gecko) (Kubuntu)",
    "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.12) Gecko/20070731 Ubuntu/dapper-security Firefox/1.5.0.12",
    "Lynx/2.8.5rel.1 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/1.2.9",
    "Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.7 (KHTML, like Gecko) Ubuntu/11.04 Chromium/16.0.912.77 Chrome/16.0.912.77 Safari/535.7",
    "Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:10.0) Gecko/20100101 Firefox/10.0 ",
    "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:73.0) Gecko/20100101 Firefox/73.0",
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
    "'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) snap Chromium/83.0.4103.116 Chrome/83.0.4103.116 Safari/537.36'"
    ]
    return random.choice(agents)

def add_header(text=True):
    base = {
        "Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7", 
        "Proxy-Connection": "keep-alive", 
        "User-Agent": random_Agent(), 
        "X-Forwarded-For":random_ip(),
    }
    if not text:
        base["Accept-Encoding"] = "gzip, deflate, br"
    return base

def open_sock5(ip, port):
    socket_bak = socket.socket
    socks.setdefaultproxy(socks.SOCKS5, ip, port)
    socket.socket = socks.socksocket

def resource_name(url):
    url = url.split('?', 1)[0] if '?' in url else url
    return url.rsplit('/', 1)[1] if '/' in url else url

def wget(url, headers={}):
    buf = {'status': False, 'msg': ''}
    try:
        req = request.Request(url)
        for key, val in add_header().items():
            req.add_header(key, val)
        for key, val in headers.items():
            req.add_header(key, val)
        with request.urlopen(req) as resp:
            if resp.status == 200:
                buf = {'status': True, 'msg': resp.read().decode("utf-8", errors='ignore')}
            else:
                buf = {'status': False, 'msg': 'status: %s, reason: %s' % (resp.status, resp.reason)}
    except URLError as e:
        logging.error('[-]URLError[%s]: %s' % (sys.exc_info()[2].tb_lineno, e))
        buf = {'status': False, 'msg': e}
    except BaseException as e:
        logging.error('[-]Error[%s]: %s' % (sys.exc_info()[2].tb_lineno, e))
        buf = {'status': False, 'msg': e}
    return buf

def save_file(url, name):
    logging.info('[-]wget %s' % url)
    result = wget(url)
    if result['status']:
        with open(name, 'w') as dst:
            dst.write(result['msg'])
        return
    raise BaseException('[-] failed to get [%s]: %s' % (url, result['msg']))

def byteunit(byte):
    return '%.2fKB' % (byte / 1024.0) if byte / 1024.0 < 1024 else ('%.2fMB' % (byte / 1024.0 / 1024.0) if (byte / 1024.0 / 1024.0) < 1024 else '%.2fGB' % (byte / 1024.0 / 1024.0 / 1024.0))

def loaddata(filename):
    try:
        tmp_buf = ''
        if os.path.exists(filename):
            with open(filename, 'r') as dst:
                tmp_buf = dst.read()
            if tmp_buf and len(tmp_buf) > 0:
                return json.loads(tmp_buf, encoding='utf-8')
        else:
            raise BaseException('file %s not exists' % filename)
    except BaseException as e:
        logging.warning('[-]Error[%s]: %s' % (sys.exc_info()[2].tb_lineno, e))
    return None

def dumpdata(filename, data):
    try:
        with open(filename, 'w') as dst:
            if isinstance(data, str):
                dst.write(data)
            else:
                dst.write(json.dumps(data, ensure_ascii=False))
        return True
    except BaseException as e:
        logging.error('[-]Error[%s]: %s' % (sys.exc_info()[2].tb_lineno, e))
        if isinstance(e, KeyboardInterrupt): raise e
    return False

def get_resource_size(url):
    '''
    获取资源大小
    '''
    '''
    req = request.Request(url)
    for key, value in add_header().items():
        req.add_header(key, value)
    req.add_header('Range', 'bytes=0-4')
    with request.urlopen(req) as resp:
        pass
    '''
    total = 0
    mt = False
    headers = add_header()
    logging.info('[-]wget %s' % url)
    resp = requests.head(url, headers=headers)
    # resp.status_code, resp.reason
    headers = resp.headers
    if resp.status_code == 206:
        rex = re.match(r'^bytes 0-4/(\d+)$', headers['Content-Range'])
        if rex:
            total = int(rex.group(1))
            mt = True
    elif resp.status_code == 200:
        total = int(headers['Content-Length'])
        num_thread = 1
    else:
        raise BaseException('status: %s, reason: %s' % (resp.status_code, resp.reason))
    if total <= 0: raise BaseException('total is Zero')
    return total
    
async def download_range(url, filename, index, result={}, range_ = (0, 0), params={}, chunk_size=1024, timeout_ = 60, text = False, mt = True, retries=30):
    exception_ = None
    clear = False
    while retries > 0:
        try:
            if not result[index]['run']:
                return
            headers = add_header(text)
            if mt:
                headers['Range'] = 'bytes=%d-%d' % (range_[0] + result[index]['size'], range_[1])
            logging.info('[%s]wget [%s]' % (index, url))
            timeout = aiohttp.ClientTimeout(total=timeout_)
            async with aiohttp.ClientSession(timeout=timeout) as session:
                async with session.get(url, headers=headers) as resp:
                    if resp.status == 206 or resp.status == 200:
                        total_size = range_[1] - range_[0]
                        curre_size = result[index]['size']
                        if not os.path.exists(filename):
                            fp = open(filename, "wb")
                            fp.truncate(total_size + 1)
                            fp.close()
                        with open(filename, "r+b") as fp:
                            fp.seek(curre_size, 0)
                            while True:
                                chunk = await resp.content.read(chunk_size)
                                if not chunk:
                                    break
                                fp.write(chunk)
                                curre_size += len(chunk)
                                result[index]['scale'] = curre_size / (total_size if total_size > 0 else curre_size)
                                result[index]['size'] = curre_size
                        result[index] = {'scale': 1, 'size': curre_size, 'run': False, 'error': None}
                    else:
                        raise BaseException('status: %s, reason: %s' % (resp.status, resp.reason))
            return
        except BaseException as e:
            logging.error('[%s] Error[%s]: %s, remain: %s' % (index, sys.exc_info()[2].tb_lineno, e, retries - 1))
            exception_ = e
            retries -= 1
    result[index]['run'] = False
    result[index]['error'] = exception_
    raise BaseException('[-]task-%s cause error: %s' % (index, exception_))

async def status(result, filename, show=True):
    tmp_file = '%s.tmp' % filename
    logging.info('[-]output file: %s' % tmp_file)
    with open(tmp_file, 'w') as dst:
        dst.write(json.dumps(result, ensure_ascii=False))
    while True:
        with open(tmp_file, 'w') as dst:
            dst.write(json.dumps(result, ensure_ascii=False))
        flag = True
        status_str = ''
        for index in range(len(result)):
            cur_status = result.get(str(index))
            if cur_status.get('run'): flag = False
            status_str += '%s(%.2f%%)%s| ' % (byteunit(cur_status.get('size')), float(cur_status.get('scale') * 100), 'o' if cur_status.get('run') else 'x')
        if show:
            print('status: %s%s' % (status_str, ' ' * (100 - len(status_str))), end='\r')
        if flag: break
        await asyncio.sleep(0.1)
    print('')

def download(url, filename, num_thread=5):
    # 恢复备份的socket
    # if socket_bak: socket.socket = socket_bak
    start_t = time.time()
    try:
        total = get_resource_size(url)
        logging.info('[+]total: %s, thread num: %s' % (byteunit(total), num_thread))
        part = total // num_thread
        result = loaddata('%s.tmp' % filename)
        if not result:
            result = {}
            for i in range(num_thread):
                result[str(i)] = {'scale': 0, 'size': 0, 'run': True, 'error': None}
        tasks = [download_range(url, '%s-%s' % (filename, index), str(index), result, (part * index + 1 if index > 0 else part * index, total if index == num_thread - 1 else part * index + part)) for index in range(num_thread)]
        tasks.append(status(result, filename))
        loop = asyncio.get_event_loop()
        loop.run_until_complete(asyncio.wait(tasks))
        for index in range(num_thread):
            if result[str(index)]['error']: 
                raise BaseException('[-]task-%s cause error: %s' % (index, result[index]['error']))
            src_path = '%s-%s' % (filename, index)
            if not os.path.exists(src_path):
                raise BaseException('[-]tmp filename %s not exists' % src_path)
        if os.path.exists(filename): os.remove(filename)
        with open(filename, 'ab') as dst:
            for index in range(num_thread):
                src_path = '%s-%s' % (filename, index)
                if os.path.exists(src_path):
                    with open(src_path, 'rb') as src:
                        dst.write(src.read())
                        os.remove(src_path)
        if os.path.exists('.%s.tmp' % filename): os.remove('.%s.tmp' % filename)
        os.rename('%s.tmp' % filename, '%s/.%s.tmp' % tuple(filename.rsplit('/', 1)))
        loop.close()
    except KeyboardInterrupt as e:
        loop.stop()
        logging.error('[-]Stop[%s]: %s' % (sys.exc_info()[2].tb_lineno, e))
        exit(-1)
    except URLError as e:
        logging.error('[-]URLError[%s]: %s' % (sys.exc_info()[2].tb_lineno, e))
    except BaseException as e:
        logging.error('[-]Error[%s]: %s' % (sys.exc_info()[2].tb_lineno, e))
    logging.info('[-]spend: %ds' % int(time.time() - start_t))
    logging.info('SUCCESS')

async def download_rang_task(task, chunk_size=1024, timeout_ = 60, text = False, mt = True, show=False):
    if task['complete'] > 0: return
    # if task['range'][1] - task['range'][0] <= task['size'] or task['complete'] > 0: return
    task['run'] = True
    headers = add_header(text)
    
    filename = task['name']
    #if task['range'][1] != task['range'][0]:
    if 'range' in task:
        headers['Range'] = 'bytes=%d-%d' % (task['range'][0] + task['size'], task['range'][1])
    if show:
        if 'range' in task:
            logging.info('[%s]wget [%s]: Range(%d-%d)' % (filename, task['url'], task['range'][0] + task['size'], task['range'][1]))
        else:
            logging.info('[%s]wget [%s]' % (filename, task['url']))
    timeout = aiohttp.ClientTimeout(total=timeout_)
    async with aiohttp.ClientSession(timeout=timeout) as session:
        async with session.get(task['url'], headers=headers) as resp:
            # 206 | 200 分开
            if resp.status in (206, 200):
                if resp.status == 206:
                    total_size = task['range'][1] - task['range'][0]
                    curre_size = task['size']
                    if not os.path.exists(filename):
                        fp = open(filename, "wb")
                        fp.truncate(total_size + 1)
                        fp.close()
                    with open(filename, "r+b") as fp:
                        fp.seek(curre_size, 0)
                        while True:
                            chunk = await resp.content.read(chunk_size)
                            if not chunk:
                                break
                            fp.write(chunk)
                            curre_size += len(chunk)
                            task['scale'] = curre_size / (total_size if total_size > 0 else curre_size)
                            task['size'] = curre_size
                if resp.status == 200:
                    total_size = int(resp.headers.get('Content-Length', 0))
                    curre_size = task['size']
                    with open(filename, "wb") as fp:
                        while True:
                            chunk = await resp.content.read(chunk_size)
                            if not chunk:
                                break
                            fp.write(chunk)
                            curre_size += len(chunk)
                            task['scale'] = curre_size / (total_size if total_size > 0 else curre_size)
                            task['size'] = curre_size
                task['scale'] = 1
                task['run'] = False
                task['complete'] = 1
            else:
                if resp.status >= 500:
                    task['complete'] = 2
                else:
                    task['complete'] = -1
                task['run'] = False
                raise BaseException('status: %s, reason: %s' % (resp.status, resp.reason))

async def download_task(tasks, result={}, retries=30, text=False):
    try:
        while not tasks.empty():
            key = tasks.get()
            while retries > 0:
                try:
                    await download_rang_task(result[key], text=text)
                    result[key]['complete'] = 1
                    break
                except asyncio.TimeoutError as e:
                    logging.error('[%s]TimeoutError: %s, remain: %s' % (key, e, retries - 1))
                except BaseException as e:
                    if result[key]['complete'] == 2:
                        logging.error('[%s]Error: %s' % (key, e))
                        break
                    logging.error('[%s]Error: %s, remain: %s' % (key, e, retries - 1))
                    retries -= 1
            retries=30
    except BaseException as e:
        logging.error('[-]Error[%s]: %s' % (sys.exc_info()[2].tb_lineno, e))

async def task_status(result, filename, show=True, speed={'beg_t': 0, 'beg_d': 0, 'dtl': 0}):
    tmp_file = '%s.tmp' % filename
    logging.info('[-]output file: %s' % tmp_file)
    dumpdata(tmp_file, result)
    while True:
        dumpdata(tmp_file, result)
        flag = True
        status_str = ''
        sum = 0.0
        total = 0
        thread_num = 0
        for key, item in result.items():
            sum += float(item.get('scale') * 100)
            total += item.get('size')
            if item['complete'] == 0 or item['complete'] == -1: flag = False
            if not item['run']: continue
            thread_num += 1
            # status_str += '%s(%.2f%%)%s| ' % (byteunit(item.get('size')), float(item.get('scale') * 100), 'o' if item.get('run') else 'x')
        cur = time.time()
        if speed['beg_t'] == 0:
            speed['beg_d'] = total
            speed['beg_t'] = cur
            
        if cur - speed['beg_t'] >= 1:
            speed['dtl'] = total - speed['beg_d'] / (cur - speed['beg_t'])
            speed['beg_d'] = total
            speed['beg_t'] = cur
        status_str = '%s(%.2f%%, %s/s) | %d' % (byteunit(total), sum / len(result), byteunit(speed['dtl']), thread_num)
        if show:
            print('status: %s%s' % (status_str, ' ' * (0 if len(status_str) > COLS - 10 else COLS - len(status_str) - 10)), end='\r')
        dumpdata('%s.status' % filename, status_str)
        if flag: break
        await asyncio.sleep(0.1)
    speed={'beg_t': 0, 'beg_d': 0, 'dtl': 0}
    logging.info('\ndownload complete')

def download_create(filename, task = queue.Queue(-1), result={}, show=True, num_thread=128, rmts=True, merge=True, text=False):
    data = None
    start_t = time.time()
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    if os.path.exists('%s.tmp' % filename):
        data = loaddata('%s.tmp' % filename)
        if data:
            task = queue.Queue(-1)
            result={}
            for key, item in data.items():
                task.put(key)
                result[key] = item
    if task.qsize() <= 0: raise BaseException('task is empty: %s' % task.qsize())
    if len(result) <= 0: raise BaseException('result is empty: %s' % result)
    tasks = [download_task(task, result, text=text) for index in range(num_thread)]
    speed={'beg_t': 0, 'beg_d': 0, 'dtl': 0}
    tasks.append(task_status(result, filename, show, speed))
    loop.run_until_complete(asyncio.wait(tasks))
    # 合并
    if merge:
        complete = [item for key, item in result.items() if item['complete'] == 1 and os.path.exists(key)]
        if len(complete) <= 0: raise BaseException('number of complete file is 0: %s' % complete)
        root, name = filename.rsplit('/', 1)
        keys = {}
        for item in complete:
            key_dir = item['name'].rsplit('/', 1)[0]
            if keys.get(key_dir, None):
                keys[key_dir].append((item['name'], item.get('key', None)))
            else:
                keys[key_dir] = [(item['name'], item.get('key', None))]
        for key, items in keys.items():
            name = '%s/%s' % (key, name)
            if os.path.exists(name): os.remove(name)
            logging.info('[-]output file: %s' % name)
            with open(name, 'ab') as dst:
                for index, item in enumerate(items, start=1):
                    if not os.path.exists(item[0]): continue
                    with open(item[0], 'rb') as src:
                        if item[1]:
                            # 解密
                            cryptor = AES.new(item[1].encode(), AES.MODE_CBC, item[1].encode())
                            dst.write(cryptor.decrypt(src.read()))
                        else:
                            dst.write(src.read())
                        if rmts:
                            os.remove(item[0])
                    status_str = '%s | (%s / %s)' % (str(item), index, len(items))
                    if show:
                        print('status: %s%s' % (status_str, ' ' * (100 - len(status_str))), end='\r')
                    dumpdata('%s.status_merge' % name, status_str)
    if os.path.exists('.%s.tmp' % filename): os.remove('.%s.tmp' % filename)
    os.rename('%s.tmp' % filename, '%s/.%s.tmp' % tuple(filename.rsplit('/', 1)))
    loop.close()
    logging.info('\n[-]SUCCESS')
    logging.info('[-]spend: %ds' % int(time.time() - start_t))
    
def download_file(url, filename, num_thread=128, blocks = 128):
    total = get_resource_size(url)
    logging.info('[+]total: %s, %d ts, thread num: %s' % (byteunit(total), blocks, num_thread))
    part = total // blocks
    task = queue.Queue(-1)
    result={}
    for index in range(blocks):
        name = '%s-%s' % (filename, index)
        task_index = {'name': name, 'url': url, 'range': (part * index + 1 if index > 0 else part * index, total if index == blocks - 1 else part * index + part), 'scale': 0, 'size': 0, 'run': False, 'complete': 0, 'error': None}
        task.put(name)
        result[name] = task_index
    download_create(filename, task, result, num_thread)

def download_m3u8(url, filename, num_thread=128, desc = {}):
    result={}
    task = queue.Queue(-1)
    download_m3u8_load(url, filename, result, task, desc)
    # print(result, desc)
    download_create('%s.mp4' % filename.rsplit('.', 1)[0], task, result, num_thread, rmts=False)
    
def download_m3u8_load(url, filename, result={}, task = queue.Queue(-1), desc={}):
    os.makedirs(filename.rsplit('/', 1)[0], exist_ok = True)
    save_file(url, filename)
    if not os.path.exists(filename):
        raise BaseException("m3u8 file not exists: %s" % filename)
    content = ''
    file_line = None
    with open(filename, 'r') as src:
        content = src.read()
    if len(content) <= 0:
        raise BaseException("Failed to download m3u8 file: \n%s" % content)
    if '\r\n' in content:
        file_line = content.split("\r\n")
    else:
        file_line = content.split("\n")
    if file_line[0] != "#EXTM3U":
        raise BaseException("it is not m3u8 file: \n%s" % content)
    tss = []
    duration = 0.0
    key = None
    with open(filename, 'w') as src:
        url_root = '%s%s' % (url[:url.index('://')+3], url[url.index('://')+3:].split('/', 1)[0])
        uri = '/%s' % url[url.index('://')+3:].split('/', 1)[1]
        for line in file_line:
            line__ = None
            if '#EXT-X-KEY' in line:
                key_uri = re.search('URI="(.*)"', line.replace("'", '"')).group(1) if '"' in line or "'" in line else re.search('URI=(.*)', line).group(1)
                if key_uri.startswith('/'):
                    key_url = '%s%s' % (url_root, key_uri)
                elif key_uri.startswith('http'):
                    key_url = key_uri
                else:
                    key_url = '%s/%s' % (url.rsplit('/', 1)[0], key_uri)
                key_path = '%s/%s' % (filename.rsplit('/', 1)[0], resource_name(key_uri))
                save_file(key_url, key_path)
                with open(key_path, 'r') as key_src:
                    key = key_src.read()
                line = '%sURI="%s"' % (line[:line.index('URI=')], key_uri if '/' not in key_uri else key_uri.rsplit('/', 1)[1])
            if '#EXTINF:' in line:
                exp = re.match('#EXTINF:(.\d*\.?\d*),', line)
                duration = duration + float(exp.group(1))
            if line.startswith('/'):
                index = 0
                line_ = line.split('/')
                uri_ = uri.split('/')
                while (index < min(len(line_), len(uri_))) and line_[index] == uri_[index]:
                    index += 1
                line = '/'.join(line_[index:])
                line__ = '%s%s' % (url_root, line)
            if line.startswith('http'):
                line = '/%s' % line[line.index('://') + 3:].split('/', 1)[1]
                line_ = line.split('/')
                uri_ = uri.split('/')
                index = 0
                while (index < min(len(line_), len(uri_))) and line_[index] == uri_[index]:
                    index += 1
                line = '/'.join(line_[index:])
                line__ = line
            src.write('%s\n' % line)
            if "#EXT" in line or len(line.strip()) == 0: continue
            if line__:
                tss.append((line__, '%s/%s' % (filename.rsplit('/', 1)[0], line)))
            else:
                tss.append(('%s/%s' % (url.rsplit('/', 1)[0], line),'%s/%s' % (filename.rsplit('/', 1)[0], line)))
    desc['duration'] = duration
    for url, name in tss:
        if resource_name(url).endswith('.m3u8'):
            download_m3u8_load(url, name, result, task, desc)
        else:
            result[name]={'name': name, 'url': url, 'range': (1, 1), 'scale': 0, 'size': 0, 'run': False, 'complete': 0, 'error': None, 'key': key}
            task.put(name)
    
def arg_list(flag, argv):
    argv_l = []
    for index in range(argv.index(flag) + 1, len(argv)):
        if argv[index].startswith('-'): break
        argv_l.append(argv[index])
    return argv_l

if __name__ == '__main__':
    help_msg = '''\
        Usage:python3 %s [-p <addr:port>] options
        -u <url> [filename]                         |下载文件
        -p <addr:port>                              |代理
        --download_m3u8 referer referer_referer     |下载hls视频
        ---------------------------------------
        dependence
        ---------------------------------------
        aiohttp==3.6.2
        pycryptodome==3.9.8
        pyjsparser==2.7.1
        PySocks==1.7.1
        requests==2.24.0
        ---------------------------------------
''' % sys.argv[0]
    if len(sys.argv) < 2:
        print(help_msg)
    else:
        if '-p' in sys.argv:
            arg_l = arg_list('-p', sys.argv)
            if len(arg_l) <= 0: raise BaseException("[-]-p [addr:port]: %s" % arg_l)
            if ':' not in arg_l[0]: raise BaseException("[-]-p [addr:port]: %s" % arg_l[0])
            addr, port = arg_l[0].split(':')
            # open_sock5('127.0.0.1', 1080)
            open_sock5(addr, int(port))
        if '-u' in sys.argv:
            arg_l = arg_list('-u', sys.argv)
            if len(arg_l) <= 0: raise BaseException("[-]-u url [filename]: %s" % arg_l)
            url = arg_l[0]
            filename = resource_name(url)
            if len(arg_l) >= 2:
                filename = arg_l[1]
            if '/' in filename:
                os.makedirs(filename.rsplit('/', 1)[0], exist_ok = True)
            # download_file(url, filename)
            download(url, filename)
            exit(0)
        if '--download_m3u8' in sys.argv:
            arg_l = arg_list('--download_m3u8', sys.argv)
            if len(arg_l) < 2: raise BaseException("[-]--download_m3u8 referer referer_referer: %s" % arg_l)
            referer = arg_l[0]
            referer_referer = arg_l[1]
            url = referer
            filename = '%s/%s/%s' % (m3u8dir, referer_referer[referer_referer.index('://') + 3:].replace(":", "_"), resource_name(url))
            # print(filename)
            # exit(1)
            download_m3u8(url, filename)
            exit(0)
        print(help_msg)